% Pravidla (termy) pro zpracování věty.
:- encoding(utf8).
:- include('categories.pl').
:- include('node_pp.pl').

parse(Sentence, IL) :- s(Sentence, Tree), tree_to_il(Tree, IL).
parse_and_print(Sentence) :-
	parse(Sentence, IL),
	pp(IL).

tree_to_il(one_word(Sem), node(Sem, [], [])).
tree_to_il(s(Subject, Verb, Objects), node(Sem, Attr, Children)) :-
	Verb = vsp(Sem, _, _, _, _),
	verb_to_attr(Verb, Attr),
	subject_info(Subject, SubCh),
	objects_info(Objects, ObjCh),
	append(SubCh, ObjCh, Children).

verb_to_attr(vsp(_, _, _, _, imperative), [mood-imp]).
verb_to_attr(vsp(_, _, _, _, Tense), [tense-Tense]) :- Tense \= imperative.

subject_info(empty, []).
subject_info(
	     Subject,
	     [subject-[SubjectNode]]
	    ) :-
	np_info(Subject, SubjectNode).
%	adj_to_adjnodes(Adj, AdjNodes),
%	add_key(adj, AdjNodes, [], Children1),
%	add_key_to_all(poss, Poss, PossWithKey),
%	objects_info(PossWithKey, Poss2),
%	append(Poss2, Children1, Children).

objects_info([], []).
objects_info(
	     [Key-NP | ObjTail],
	     [Key-[Node] | ChTail]
	    ) :-
	np_info(NP, Node),
	objects_info(ObjTail, ChTail).
objects_info(
	     [Key-adv(Sem) | ObjTail],
	     [Key-[node(Sem, [], [])] | ChTail]
	    ) :-
	objects_info(ObjTail, ChTail).

np_info(np(Adj, Poss, Sem, Num, Gender, Case), node(Sem, [num-Num, gen-Gender, case-Case], Children)) :-
	adj_to_adjnodes(Adj, AdjNodes),
	add_key(adj, AdjNodes, [], Children1),
	np_info(Poss, PossNodes),
	add_key(poss, PossNodes, Children1, Children).

np_info([], []).
np_info([NP | Tail], [Node | NodeTail]) :- np_info(NP, Node), np_info(Tail, NodeTail).

adj_to_adjnodes([], []).
adj_to_adjnodes([A | T], [node(A, [], []) | TN]) :- adj_to_adjnodes(T, TN).

add_key(_, [], L, L).
add_key(Key, [Head | Tail], L, [Key-[Head | Tail] | L]).

add_key_to_all(_, [], []).
add_key_to_all(Key, [Hd|Tl], [Key-Hd|KeyTl]) :- add_key_to_all(Key, Tl, KeyTl).

%
sentence(one_word(yes)) --> [ano].
sentence(one_word(no)) --> [ne].
sentence(
	 s(np([], [], Person, Num, Gender, nom),
	 vsp(Sem, Num, Person, Gender, Tense), Objects), S, L
	) :-
	sentence_h(s(empty, vsp(Sem, Num, Person, Gender, Tense), Objects), S, L).
sentence(s(Subject, Verb, Objects), S, L) :-
	sentence_h(s(Subject, Verb, Objects), S, L),
	Subject \= empty.

sentence_h(s(Subject, Verb, Objects), S, L) :-
	sentence_part(s(Subject, Verb, Objects), 0, [], e, S, L),
	sv_agreement(Subject, Verb).

% +Sentence věta
% ?T výsledný strom pro větu
s(Sentence, T) :- sentence(T, Sentence, []).

% Zjistí, jestli daná věta je správná.
% +Sentence
s(Sentence) :- sentence(_, Sentence, []).

% Parsování věty po částech.
sentence_part(s(empty, empty, []), _, [], _) --> [].
sentence_part(s(Subject, Verb, Objects), 1, [], V2) -->
	snd_place(Snd_place), sentence_part(s(Subject, Verb, Objects), 2, Snd_place, V2).
sentence_part(s(np(A, Poss, N, Num, Gender, nom), Verb, Objects), K, Snd_place, V2) -->
	{ K1 is K + 1 },
	np(A, Poss, N, Num, Gender, nom),
	sentence_part(s(empty, Verb, Objects), K1, Snd_place, V2).
sentence_part(s(Subject, Verb, Objects), K, Snd_place, e) -->
	{ K1 is K + 1 },
	[V_fon], sentence_part(s(Subject, empty, Objects), K1, [], Verb),
	{
	 verb(V, Sem),
	 Verb=vsp(Sem, Num, Person, Gender, Tense),
	 vform(V, Tense, Num, Person, Gender, [V_fon | Snd_place] )
	}.
sentence_part(s(Subject, Verb, [O | Objects]), K, Snd_place, V2) -->
	{ K1 is K + 1 },
	(object(O, V2); object(O, Verb)),
	sentence_part(s(Subject, Verb, Objects), K1, Snd_place, V2).
sentence_part(s(Subject, Verb, Objects), 0, [], e) -->
	[V_fon],
	snd_place(Snd_place),
	sentence_part(s(Subject, empty, Objects), 2, [], Verb),
	{
	 verb(V, Sem),
	 Verb=vsp(Sem, Num, Person, Gender, Tense),
	 vform(V, Tense, Num, Person, Gender, [V_fon | Snd_place] )
	}.

% Shoda mezi podmětem a přísudkem.
sv_agreement(empty, vsp(_, _, _, _, _)). % Prázdný podmět se shoduje s každým neprázdným slovesem.
sv_agreement(np(_, _, _, Num, Gender, _), vsp(_, Num, 3, Gender, _)).

object(Key-np(A, Poss, N, Num, Gender, Case), Verb) -->
	{ Verb=vsp(V, _, _, _, _), val(V, Val), member(Key-Case, Val) },
	np(A, Poss, N, Num, Gender, Case).
object(free-np(A, Poss, N, Num, Gender, Case), vsp(_, _, _, _, _)) -->
	prep_phrase(pp(A, Poss, N, Num, Gender, Case)).
object(how-adv(Sem), vsp(_, _, _, _, _)) --> [Adv], { adv(Adv, Sem) }.

% Co všechno najdeme ve větě na druhém místě...
snd_place([se]) --> [se].
snd_place([si]) --> [si].
snd_place([jsi, se]) --> [ses].
snd_place([jsi, si]) --> [sis].
snd_place([Aux]) --> [Aux], { auxiliary_verb(Aux) }.
snd_place([Aux | Tail]) --> [Aux], snd_place(Tail), { auxiliary_verb(Aux) }.
auxiliary_verb(jsem).
auxiliary_verb(jsi).
auxiliary_verb(jsme).
auxiliary_verb(jste).
auxiliary_verb(bych).
auxiliary_verb(bys).
auxiliary_verb(by).
auxiliary_verb(bychom).
auxiliary_verb(byste).

% Pády a předložkové pády.
% Povolíme následující konstrukci: [předložka] [přídavné jméno]* [podstatné jméno].

np(A, [], N, Num, Gender, Case) --> np_adjectives(A, Num, Gender, Case), np_noun(N, Num, Gender, Case).
np(A, [np(APoss, PPoss, NPoss, NumPoss, GenderPoss, genitive)], N, Num, Gender, Case) -->
	np_adjectives(A, Num, Gender, Case),
	np_noun(N, Num, Gender, Case),
	np(APoss, PPoss, NPoss, NumPoss, GenderPoss, genitive).
np([HA|TA], [], reference, Num, Gender, Case) --> np_adjectives([HA|TA], Num, Gender, Case).
np(A, Poss, N, Num, Gender, Case) -->
	[Prep_fon], np(A, Poss, N, Num, Gender, Form_case), { prep_case(Case, Prep_fon, Form_case) }.

np_adjectives([], _, _, _) --> []. % Prázdný řetězec přídavných jmen budeme vždycky považovat za správný.
np_adjectives([Sem | Tail], Num, Gender, Case) -->
	[A_fon], np_adjectives(Tail, Num, Gender, Case),
	{ adj(A, Sem), case(A, Num, Gender, Case, A_fon) }.

np_noun(Sem, Num, Gender, Case) --> [N_fon], { noun(N, Gender, Sem), case(N, Num, Case, N_fon) }.

% Volná doplnění - čili to, co můžeme připojit ke každému slovesu. Náhodou to musí předložková fráze.
prep_phrase(pp(A, Poss, N, X, Y, Case)) --> np(A, Poss, N, X, Y, Case), { fo_case(Case) }.

% Pády, které můžou být ve volných doplněních.
fo_case(inessivus). % v České republice v Praze v Břevnově
fo_case(supe). % na stole na jevišti na hlavním nádraží na Moravě

% Slovník.
adj('úspěšný', successful).
adj('zajímavý', interesting).
adj('dobrý', good).
adj('levý', left).
adj('pravý', right).
adj('horní', top).
adj('dolní', bottom).
adj('střední', middle).
adj('obě', both).
adj('červený', red).
adj('zelený', green).
adj('žlutý', yellow).
adj('modrý', blue).
adj('rychlý', fast).
adj('pomalý', slow).

noun(projekt, masci, project).
noun(student, masca, student).
noun(praha, fem, prague).
noun(ucho, neut, ear).
noun(dioda, fem, led).
noun('světlo', neut, led).
noun('světýlko', neut, led).
noun('bříško', neut, belly).
noun('barva', fem, color).

adv('červeně', red).
adv('zeleně', green).
adv('žlutě', yellow).
adv('modře', blue).
adv('rychle', fast).
adv('pomalu', slow).

%prep(na, locative).

verb('dělat', do).
verb('pracovat', work).
verb('mávnout', wave).
verb('rozsvítit', switch_on).
verb('změnit', change).

val(switch_on, [dir_obj-acc]).
val(work, [on-supe]).
val(wave, [with-inst]).
val(change, [dir_obj-acc, from-elativus, into-sublativus]).

% Pravidla - přídavná jména.

% Obecné pravidlo - pro přídavná jména vocative == nom.
case(Adj, Num, Gender, vocative, Res) :-
	adj(Adj, _), case(Adj, Num, Gender, nom, Res).
case(Adj, Num, Gender, Case, Res) :-
	adj(Adj, _),
	sub_atom(Adj, _, _, 0, 'ý'),
	(
	 (
	  Num=pl, Gender=masca, Case=nom,
	  masca_nompl_end(Adj, Res),
	  !
	 );
	 (
	  sub_atom(Adj, 0, _, 1, Stem),
	  adj_form_hard(Stem, Num, Gender, Case, Res)
	 )
	).
case(Adj, Num, Gender, Case, Res) :-
	adj(Adj, _),
	sub_atom(Adj, _, _, 0, 'í'),
	sub_atom(Adj, 0, _, 1, Stem),
	adj_form_soft(Stem, Num, Gender, Case, Res).

case(obě, dual, _, nom, 'obě').
case(obě, dual, _, inst, 'oběma').

masca_nompl_end(['rý'-'ří', 'ký'-'cí']).
masca_nompl_end(Adj, Res) :-
	masca_nompl_end(L),
	masca_nompl_end(Adj, Res, L).
masca_nompl_end(Adj, Res, [Key-Value | Tail]) :-
	(
	 sub_atom(Adj, _, L, 0, Key),
	 sub_atom(Adj, _, _, L, Stem),
	 concat(Stem, Value, Res)
	);
	masca_nompl_end(Adj, Res, Tail).

adj_form_hard(Stem, sg, Gender, nom, Res) :-
	(masc(Gender), concat(Stem, 'ý', Res));
	(Gender=fem, concat(Stem, 'á', Res));
	(Gender=neut, concat(Stem, 'é', Res)).
adj_form_hard(Stem, sg, Gender, genitive, Res) :-
	nofem(Gender), concat(Stem, 'ého', Res).
adj_form_hard(Stem, sg, Gender, dative, Res) :-
	nofem(Gender), concat(Stem, 'ému', Res).
adj_form_hard(Stem, sg, Gender, inst, Res) :-
	nofem(Gender), concat(Stem, 'ým', Res).
adj_form_hard(Stem, sg, Gender, locative, Res) :-
	nofem(Gender), concat(Stem, 'ém', Res).
adj_form_hard(Stem, sg, masca, acc, Res) :-
	concat(Stem, 'ého', Res).
adj_form_hard(Stem, sg, masci, acc, Res) :-
	concat(Stem, 'ý', Res).
adj_form_hard(Stem, sg, neut, acc, Res) :-
	concat(Stem, 'é', Res).
adj_form_hard(Stem, sg, fem, Case, Res) :-
	(Case=genitive; Case=dative; Case=locative), concat(Stem, 'é', Res).
adj_form_hard(Stem, sg, fem, Case, Res) :-
	(Case=acc; Case=inst), concat(Stem, ou, Res).
adj_form_hard(Stem, pl, Gender, nom, Res) :-
	(Gender=masca, concat(Stem, 'í', Res));
	((Gender=masci, Gender=fem), concat(Stem, 'é', Res));
	(Gender=neut, concat(Stem, 'á', Res)).
adj_form_hard(Stem, pl, _, Case, Res) :-
	(Case=genitive; Case=locative), concat(Stem, 'ých', Res).
adj_form_hard(Stem, pl, _, dative, Res) :-
	concat(Stem, 'ým', Res).
adj_form_hard(Stem, pl, _, inst, Res) :-
	concat(Stem, 'ými', Res).
adj_form_hard(Stem, pl, masca, acc, Res) :-
	concat(Stem, 'é', Res).
adj_form_hard(Stem, pl, Gender, acc, Res) :-
	(Gender=masci; Gender=fem; Gender=neut), adj_form_hard(Stem, pl, Gender, nom, Res).
adj_form_hard(Stem, dual, neut, inst, Res) :-
	concat(Stem, 'ýma', Res).
adj_form_hard(Stem, dual, neut, Case, Res) :-
	(Case=nom; Case=genitive; Case=dative; Case=acc; Case=locative),
	adj_form_hard(Stem, pl, fem, Case, Res).

adj_form_soft(Stem, sg, fem, _, Res) :-
	concat(Stem, 'í', Res).
adj_form_soft(Stem, sg, Gender, genitive, Res) :-
	nofem(Gender), concat(Stem, 'ího', Res).
adj_form_soft(Stem, sg, Gender, dative, Res) :-
	nofem(Gender), concat(Stem, 'ímu', Res).
adj_form_soft(Stem, sg, masca, acc, Res) :-
	concat(Stem, 'ího', Res).
adj_form_soft(Stem, sg, Gender, acc, Res) :-
	(Gender=masci; Gender=neut), concat(Stem, 'í', Res).
adj_form_soft(Stem, sg, Gender, Case, Res) :-
	nofem(Gender), (Case=inst; Case=locative), concat(Stem, 'ím', Res).
adj_form_soft(Stem, pl, _, Case, Res) :-
	(Case=nom; Case=acc), concat(Stem, 'ím', Res).
adj_form_soft(Stem, pl, _, Case, Res) :-
	(Case=genitive; Case=locative), concat(Stem, 'ích', Res).
adj_form_soft(Stem, pl, _, dative, Res) :-
	concat(Stem, 'ím', Res).
adj_form_soft(Stem, pl, _, inst, Res) :-
	concat(Stem, 'ími', Res).
adj_form_soft(Stem, dual, neut, inst, Res) :-
	concat(Stem, 'íma', Res).
adj_form_soft(Stem, dual, neut, Case, Res) :-
	(Case=nom; Case=genitive; Case=dative; Case=acc; Case=locative),
	adj_form_soft(Stem, pl, fem, Case, Res).

% Pravidla - podstatná jména.
lock_nom('obě').

%case(N, sg, nom, _) :- lock_nom(N), !, fail.
case(N, sg, nom, N).

case(projekt, sg, locative, projektu).
case(projekt, pl, nom, projekty).
case(projekt, pl, locative, projektech).

case(praha, sg, locative, praze).

case(student, pl, nom, studenti).

case(barva, sg, genitive, barvy).
case(barva, sg, acc, barvu).

case(N, sg, acc, Form) :- noun(N, neut, _), case(N, sg, nom, Form).

case(ucho, sg, inst, uchem).
case(ucho, sg, locative, uchu).
case(ucho, dual, inst, 'ušima').

case('bříško', sg, locative, 'bříšku').

case(dioda, sg, acc, diodu).
case(dioda, sg, genitive, diody).

case('světlo', sg, genitive, 'světla').
case('světýlko', sg, genitive, 'světýlka').

% Časování sloves.
vform(V, Tense, dual, Person, neut, VFL) :-
	vform(V, Tense, pl, Person, fem, VFL).

% Časování sloves s koncovkou -ovat.
vform(V, Tense, Num, Person, Gender, [VF]) :-
	verb(V, _),
	sub_atom(V, _, _, 0, ovat),
	sub_atom(V, 0, _, 4, Stem),
	vform_ovat(Stem, Tense, Num, Person, Gender, VF).

% Časování sloves s koncovkou -nout.
vform(V, Tense, Num, Person, Gender, [VF]) :-
	verb(V, _),
	sub_atom(V, _, _, 0, nout),
	sub_atom(V, 0, _, 4, Stem),
	vform_nout(Stem, Tense, Num, Person, Gender, VF).

% Časování sloves s koncovkou -ítit.
vform(V, Tense, Num, Person, Gender, [VF]) :-
	verb(V, _),
	sub_atom(V, _, _, 0, 'ítit'),
	sub_atom(V, 0, _, 4, Stem),
	vform_itit(Stem, Tense, Num, Person, Gender, VF).

vform(změnit, Tense, Num, Person, Gender, [VF]) :-
	vform_nit('změ', Tense, Num, Person, Gender, VF).

vform(V, past, Num, Person, Gender, [VF | Aux]) :-
	verb(V, _),
	past_participle(V, Num, Gender, VF),
	auxiliary_past(Num, Person, Aux).

past_participle(V, Num, Gender, VF) :-
	sub_atom(V, _, _, 0, ovat),
	sub_atom(V, 0, _, 1, Stem),
	past_participle_ovat(Stem, Num, Gender, VF).

auxiliary_past(sg, 1, [jsem]).
auxiliary_past(sg, 2, [jsi]).
auxiliary_past(_, 3, []).
auxiliary_past(pl, 1, [jsme]).
auxiliary_past(pl, 2, [jste]).

vform_ovat(Stem, present, Num, Person, _, VF) :-
	(Num = sg, Person = 1, (concat(Stem, uju, VF); concat(Stem, uji, VF)));
	(Num = sg, Person = 2, concat(Stem, 'uješ', VF));
	(Num = sg, Person = 3, concat(Stem, uje, VF));
	(Num = pl, Person = 1, concat(Stem, ujeme, VF));
	(Num = pl, Person = 2, concat(Stem, ujete, VF));
	(Num = pl, Person = 3, concat(Stem, 'ují', VF)).

vform_ovat(Stem, imperative, Num, Person, _, VF) :-
	(Num = sg, Person = 2, concat(Stem, uj, VF));
	(Num = pl, Person = 1, concat(Stem, ujme, VF));
	(Num = pl, Person = 2, concat(Stem, ujte, VF)).

past_participle_ovat(Stem, Num, Gender, VF) :-
	(Num = sg, masc(Gender), concat(Stem, l, VF));
	(Num = sg, Gender = fem, concat(Stem, la, VF));
	(Num = sg, Gender = neut, concat(Stem, lo, VF));
	(Num = pl, Gender = masca, concat(Stem, li, VF));
	(Num = pl, (Gender = masci; Gender = fem), concat(Stem, ly, VF));
	(Num = pl, Gender = neut, concat(Stem, la, VF)).

vform_nout(Stem, present, Num, Person, _, VF) :-
	(Num = sg, Person = 1, concat(Stem, nu, VF));
	(Num = sg, Person = 2, concat(Stem, 'neš', VF));
	(Num = sg, Person = 3, concat(Stem, ne, VF));
	(Num = pl, Person = 1, concat(Stem, neme, VF));
	(Num = pl, Person = 2, concat(Stem, nete, VF));
	(Num = pl, Person = 3, concat(Stem, nou, VF)).

vform_nout(Stem, imperative, Num, Person, _, VF) :-
	(Num = sg, Person = 2, concat(Stem, ni, VF));
	(Num = pl, Person = 1, concat(Stem, 'něme', VF));
	(Num = pl, Person = 2, concat(Stem, 'něte', VF)).

vform_itit(Stem, present, Num, Person, _, VF) :-
	(Num = sg, Person = 1, concat(Stem, 'ítím', VF));
	(Num = sg, Person = 2, concat(Stem, 'ítíš', VF));
	(Num = sg, Person = 3, concat(Stem, 'ítí', VF));
	(Num = pl, Person = 1, concat(Stem, 'ítíme', VF));
	(Num = pl, Person = 2, concat(Stem, 'ítíte', VF));
	(Num = pl, Person = 3, concat(Stem, 'ítí', VF)).

vform_itit(Stem, imperative, Num, Person, _, VF) :-
	(Num = sg, Person = 2, concat(Stem, 'iť', VF));
	(Num = pl, Person = 1, concat(Stem, 'iťme', VF));
	(Num = pl, Person = 2, concat(Stem, 'iťte', VF)).

vform_nit(Stem, present, Num, Person, _, VF) :-
	(Num = sg, Person = 1, concat(Stem, 'ním', VF));
	(Num = sg, Person = 2, concat(Stem, 'níš', VF));
	(Num = sg, Person = 3, concat(Stem, 'ní', VF));
	(Num = pl, Person = 1, concat(Stem, 'níme', VF));
	(Num = pl, Person = 2, concat(Stem, 'níte', VF));
	(Num = pl, Person = 3, concat(Stem, 'ní', VF)).

vform_nit(Stem, imperative, Num, Person, _, VF) :-
	(Num = sg, Person = 2, concat(Stem, 'ň', VF));
	(Num = pl, Person = 1, concat(Stem, 'ňme', VF));
	(Num = pl, Person = 2, concat(Stem, 'ňte', VF)).

% Test.

test_grammar :- test_grammar_ok, test_grammar_nook.

test_grammar_ok :-
	test_pred(s(['pracuji', na, 'úspěšných', projektech])),
	test_pred(s([pracuje, na, 'dobrých', projektech])),
	test_pred(s([studenti, 'pracují', na, projektech])),
	test_pred(s([student, pracuje, na, projektu])),
	test_pred(s([student, pracoval, na, projektu])),
	test_pred(s([v, praze, na, 'úspěšných', projektech, pracoval])),
	test_pred(s([v, praze, jsem, na, 'úspěšných', projektech, pracoval])),
	test_pred(s(['mávni', uchem])),
	test_pred(s(['mávni', 'levým', uchem])),
	test_pred(s(['mávni', 'pravýma', 'ušima', v, praze])), % věta je správná, i když nesmyslná
	test_pred(s([na, 'levém', uchu, v, praze, 'mávni', 'pravýma', 'ušima'])),
	test_pred(s(['rozsviťte'])),
	test_pred(s(['rozsviť', levou, diodu])),
	test_pred(s(['obě', 'uši', 'pracují', na, projektu])),
	test_pred(s([na, projektu, pracovaly, 'obě', 'uši'])),
	test_pred(s(['červeně', 'rozsviť', 'horní', 'světýlko', na, 'bříšku'])),
	test_pred(s(['dobří', studenti, 'rozsvítí', 'horní', 'světýlko', na, 'bříšku', v, praze])),
	test_pred(s(['změň', barvu, 'horního', 'levého', 'světla', na, modrou])),
	true.

test_grammar_nook :-
	test_pred(\+ s([student, 'pracují', na, 'úspěšných', projektech])), % neshoda podmětu a přísudku
	test_pred(\+ s([student, pracuje, na, 'dobrá', projektech])), % špatný pád přídavného jména
	test_pred(\+ s([student, pracuje, na, 'úspěšných', projektu])),
				% neshoda podstatného a přídavného jména
	test_pred(\+ s([ty, pracuji, na, 'úspěšných', projektech])), % neshoda podmětu a přísudku
	test_pred(\+ s([student, jsem, pracoval, na, projektu])), % neshoda podmětu a přísudku
	test_pred(\+ s([v, praze, na, 'úspěšných', projektech, pracoval, jsem])), % nesprávný slovosled
	test_pred(\+ s(['mávni', 'levými', 'ušima'])), % neshoda v čísle
	test_pred(\+ s([pracuj, levou, diodu])), % nekorektní naplnění valenčního rámce
	test_pred(\+ s([na, projektu, pracovala, 'obě', 'uši'])), % neshoda podmětu s přísudkem (duál)
	%test_pred(\+ s(['dobrí', studenti, 'rozsvítí', 'horní', 'světýlko', na, 'bříšku', v, praze])), % dobrí místo dobří
	true.

% Vypíše testovaný predikát a výsledek testu.
test_pred(X) :- print(X), (X, !, print(': ok\n')); (print(': FAILED\n'), fail).
